/*
*设计思路
*  元素必须挂载到body上，这样才不会被其他元素的overflw：hidden给遮挡住
*/
<template>
  <div class="relative" ref="popoverref" @focus="getfocus" @blur="losefocus" v-click-away="closepop">
    <!--    冒泡插槽-->
    <div v-if="visible" ref="contentref" class="absolute transform inline-block bg-white border z-20"
         :class="{'-translate-y-full':thisposition==='top','-translate-x-full':thisposition==='left'}"
         @click.stop="thisonClick">
      <slot name="content"></slot>
      <!--      那个三角形-->
      <div ref="sanjiaoxingref" class="absolute transform block bg-white border-t border-l w-2 h-2 z-10"
           :class="{'-top-1 left-1/2 -translate-x-1/2 rotate-45':thisposition==='bottom',
           '-bottom-1 left-1/2 -translate-x-1/2 -rotate-135':thisposition==='top',
           'top-1/2 -translate-y-1/2 -right-1 rotate-135':thisposition==='left',
           '-left-1 top-1/2 -translate-y-1/2 -rotate-45':thisposition==='right'}"></div>
    </div>
    <!--    默认插槽-->
    <span class="w-full" ref="triggerref" @click.stop="thisonClick">
      <slot></slot>
    </span>
  </div>
</template>

<script>
import {defineComponent, ref, onMounted, onBeforeUnmount, nextTick} from "vue";
import {mixin as VueClickAway} from "vue3-click-away";

export default defineComponent({
  name: "popover",
  mixins: [VueClickAway],
  props: {
    // 触发方式
    trigger: {
      type: String,
      default: "hover",
      // hold的意思是按住鼠标左键不放
      validator(value) {
        return ["hover", "click", "focus", "hold", "default"].indexOf(value) > -1
      }
    },
    // 定位位置
    position: {
      type: String,
      default: "bottom",
      validator(value) {
        return ["top", "left", "bottom", "right"].indexOf(value) > -1
      },
    },
    samewidth: {
      type: Boolean,
      default: false,
    },
  },
  setup(prop, context) {
    // 三个ref
    const popoverref = ref(null)
    const contentref = ref(null)
    const triggerref = ref(null)
    // 增加一个三角形的ref
    const sanjiaoxingref = ref(null)
    // 是否显示冒泡卡片
    const visible = ref(false)
    // 定位
    const thisposition = ref(prop.position)

    // 最重要的函数，为pop进行定位
    const positionContent = () => {
      document.body.appendChild(contentref.value)
      const {width, height, top, left} = triggerref.value.getBoundingClientRect()
      let width2, height2
      if (prop.samewidth) {
        contentref.value.style.width = `${width}px`
        width2 = width
        height2 = contentref.value.offsetHeight
      } else {
        width2 = contentref.value.offsetWidth
        height2 = contentref.value.offsetHeight
      }

      let dingwei = {
        top: {
          top: top + window.scrollY - 5,
          left: left + window.scrollX + width / 2 - width2 / 2
        },
        bottom: {
          top: top + height + window.scrollY + 5,
          left: left + window.scrollX + width / 2 - width2 / 2
        },
        left: {
          top: top + window.scrollY + height / 2 - height2 / 2,
          left: left + window.scrollX - 5
        },
        right: {
          top: top + window.scrollY + height / 2 - height2 / 2,
          left: left + width + window.scrollX + 5
        },
      }
      contentref.value.style.left = `${dingwei[prop.position].left}px`
      contentref.value.style.top = `${dingwei[prop.position].top}px`
    }

    // 点击方法
    const thisonClick = (e) => {
      console.log("popover收到点击事件")
      if (prop.trigger === "click") {
        if (visible.value === true) {
          closepop()
        } else {
          showpop()
        }
      }
      e.preventDefault();
    }
    // 获取焦点方法
    const getfocus = () => {
      if (prop.trigger === "focus") {
        showpop()
      }
    }
    // 失去焦点方法
    const losefocus = () => {
      if (prop.trigger === "focus") {
        closepop()
      }
    }
    // 显示
    const showpop = () => {
      // 必须先显示，然后才能获取ref，如果反过来，就获取不到ref
      visible.value = true
      nextTick(() => {
        positionContent()
      })
    }
    // 隐藏
    const closepop = () => {
      // 预留100毫秒的时间，100毫秒后，如果鼠标没有放到pop上，就关闭
      let status = false
      if (contentref.value) {
        contentref.value.addEventListener("mouseenter", () => {
          status = true
        })
        contentref.value.addEventListener("mouseleave", () => {
          doClose()
        })
      }
      setTimeout(() => {
        if (!status) {
          visible.value = false
        }
      }, 50)
    }
    // 立刻隐藏
    const doClose = () => {
      visible.value = false
    }
    // 生命周期
    onMounted(() => {
      // 初始化时，根据触发方式，绑定方法
      if (prop.trigger === "hold") {
        popoverref.value.addEventListener("mousedown", showpop)
        popoverref.value.addEventListener("mouseup", closepop)
      } else if (prop.trigger === "hover") {
        popoverref.value.addEventListener("mouseenter", showpop)
        popoverref.value.addEventListener("mouseleave", closepop)
      }
      // 滚动时，不显示pop
      window.addEventListener("scroll", closepop)
      // 窗口大小变化时，不显示pop
      window.addEventListener('resize', closepop)
    })
    onBeforeUnmount(() => {
      // 销毁前，移除监听
      if (prop.trigger === "hold") {
        popoverref.value.removeEventListener("mousedown", showpop)
        popoverref.value.removeEventListener("mouseup", closepop)
      } else if (prop.trigger === "hover") {
        popoverref.value.removeEventListener("mouseenter", showpop)
        popoverref.value.removeEventListener("mouseleave", closepop)
      }
      // 所有监听移除
      window.removeEventListener("scroll", closepop)
      window.removeEventListener("resize", closepop)
    })

    return {
      getfocus,
      losefocus,
      thisonClick,
      visible,
      popoverref,
      contentref,
      triggerref,
      thisposition,
      closepop,
      sanjiaoxingref,
      showpop,
      doClose,
    }
  },
  components: {},
})
</script>

<style scoped>
</style>